package com.mathdroid.gui.main.browser;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;

import com.mathdroid.core.DroidStringWorker;
import com.mathdroid.exceptionhandling.ExceptionHandler;
import com.mathdroid.gui.ImageLoader;
import com.mathdroid.gui.main.character.ItemSlotPopup;
import com.mathdroid.model.Model;
import com.mathdroid.model.enums.EItemSlot;
import com.mathdroid.model.enums.EItemType;
import com.mathdroid.model.items.Item;

/**
 * This context menu is shown, if a item icon in the browser is clicked.
 */
public class BrowserContextMenu extends JPopupMenu {

	/**
	 * Default UID.
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * The string helper.
	 */
	private final DroidStringWorker sw = new DroidStringWorker();
	
	/**
	 * The height of the label.
	 */
	private static final int TEXT_HEIGHT = 20;
	
	/**
	 * The additional horizontal space around the label.
	 */
	private static final int TEXT_HORIZONTAL_SPACE = 20;
	
	/**
	 * The constraints for this UI components.
	 */
	private GridBagConstraints c = new GridBagConstraints();
	
	/**
	 * The space between UI components.
	 */
	private static final int SPACE = 2;
	
	/**
	 * The item slot to put the item in, if "popup" is not null.
	 */
	private EItemSlot slot = null;
	
	/**
	 * Create a new context menu.
	 * @param i The item that was clicked.
	 * @param e The mouse event that occured while clicking.
	 * @param popup The item slot popup, can be null.
	 * @param s The slot to put the module in, if popup is not null.
	 */
	public BrowserContextMenu(final Item i, final MouseEvent e, 
			final ItemSlotPopup popup, final EItemSlot s) {
		
		this.slot = s;
		
		this.setLayout(new GridBagLayout());

		this.buildNameLabel(i);
		this.buildTorheadButton(i);
		this.buildEquipToSlotButtons(i, popup);
		
		this.show(e.getComponent(), e.getX(), e.getY());
	}

	/**
	 * Build the buttons to equip the item to the possible slots.
	 * @param i The item.
	 * @param popup The PopUp parent UI, can be null. 
	 */
	private void buildEquipToSlotButtons(
			final Item i, final ItemSlotPopup popup) {
		
		if (i.getSlots().size() > 0 || i.getType() == EItemType.MODULE) {
			/*
			 * The item is no module.
			 */
			if (i.getType() != EItemType.MODULE) {
				c.insets = new Insets(0, 0, 0, 0);
				c.gridy = 2;
				this.add(new JSeparator(), c);
				
				c.insets = new Insets(SPACE, SPACE, SPACE, SPACE);
				int y = 3;
				
				for (EItemSlot s : i.getSlots()) {
					/*
					 * The following block is used if the context menu is shown 
					 * by the "main" item browser to the right. In the main 
					 * browser there can be multiple item destinations.
					 */
					if (popup == null) {
						/*
						 * Maybe the item is an "Implant" or "Relic", so 
						 * there are two slots each.
						 */
						if (s.toString().equals(
								EItemSlot.IMPLANT_1.toString())) {
							/* Implant slot. */
							c.gridy = y;
							this.add(this.buildEquipButton(
									i, EItemSlot.IMPLANT_1, popup, 
									EItemSlot.IMPLANT_1 + " (1)"), c);
							y++;
							c.gridy = y;
							this.add(this.buildEquipButton(
									i, EItemSlot.IMPLANT_2, popup, 
									EItemSlot.IMPLANT_2 + " (2)"), c);
						} else if (s.toString()
								.equals(EItemSlot.RELIC_1.toString())) {
							/* Relic slot. */
							c.gridy = y;
							this.add(this.buildEquipButton(
									i, EItemSlot.RELIC_1, popup, 
									EItemSlot.RELIC_1 + " (1)"), c);
							y++;
							c.gridy = y;
							this.add(this.buildEquipButton(
									i, EItemSlot.RELIC_2, popup, 
									EItemSlot.RELIC_2 + " (2)"), c);
						} else {
							/* Other (unique) slots. */
							c.gridy = y;
							this.add(this.buildEquipButton(
									i, s, popup, s.toString()), c);
						}
					/*
					 * The following block is used if the context menu is shown 
					 * by an item browser PopUp. In a PopUp the item 
					 * destination is only one.
					 */
					} else {
						c.gridy = y;
						this.add(this.buildEquipButton(
								i, popup.getSlot(), popup, s.toString()), c);
					}
					y++;
				}
			} else {
				/*
				 * The item is a module and the browser is in a popup.
				 * Modules in a non-popup browser can't be equipped with a
				 * context menu, because they have no destination.
				 */
				if (popup != null) {
					c.insets = new Insets(0, 0, 0, 0);
					c.gridy = 2;
					this.add(new JSeparator(), c);
					
					c.insets = new Insets(SPACE, SPACE, SPACE, SPACE);
					int y = 3;
					
					c.gridy = y;
					this.add(this.buildEquipButton(
						i, popup.getSlot(), popup, this.slot.toString()), c);
				}
			}
		}
	}

	/**
	 * Build the label with the name of the item.
	 * @param i The item.
	 */
	private void buildNameLabel(final Item i) {
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1;
		c.insets = new Insets(SPACE, SPACE, SPACE, SPACE);
		c.gridy = 0;
		this.add(this.buildLabel(i.getName()), c);
	}

	/**
	 * Build the button to open the Torhead URL of the item.
	 * @param i The item.
	 */
	private void buildTorheadButton(final Item i) {
		c.insets = new Insets(SPACE, SPACE, SPACE, SPACE);
		c.gridy = 1;
		JButton torhead = new JButton("Open in Torhead");
		torhead.setIcon(new ImageLoader().getImage("torhead"));
		torhead.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				try {
					java.awt.Desktop.getDesktop()
					.browse(new URI("http://www.torhead.com/item/" 
							+ i.getTorHeadID()));
				} catch (IOException ex) {
					ExceptionHandler.report("Can't connect to Torhead.", ex);
					ex.printStackTrace();
				} catch (URISyntaxException ex) {
					ExceptionHandler.report("Malformed Torhead URL.", ex);
					ex.printStackTrace();
				}
			}
		});
		this.add(torhead, c);
	}
	
	/**
	 * Build a simple label that shows the name of the item.
	 * @param name Name of the item.
	 * @return A new JLabel.
	 */
	private JLabel buildLabel(final String name) {
		final JLabel lbl = new JLabel(name);
		
		lbl.setHorizontalAlignment(SwingConstants.CENTER);
		lbl.setPreferredSize(
				new Dimension(this.sw.textWidth(lbl, name) 
						+ TEXT_HORIZONTAL_SPACE, TEXT_HEIGHT));
		lbl.setForeground(Color.GRAY);
		
		return lbl;
	}

	/**
	 * Build a button to equip the selected item.
	 * @param i The selected item.
	 * @param s The destination slot.
	 * @param popup The item slot popup, can be null.
	 * @param slotName The name of the slot (for multiple slots).
	 * @return A new button.
	 */
	private JButton buildEquipButton(final Item i, final EItemSlot s, 
			final ItemSlotPopup popup, final String slotName) {
		final JButton menuItem = 
			new JButton("Equip to \"" + slotName + "\"");
		menuItem.setIcon(new ImageLoader().getImage("add"));

		final BrowserContextMenu bcm = this;
		
		menuItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				if (i.getType() == EItemType.MODULE) {
					Model.equipModule(i, s);
				} else {
					if (!Model.equipItem(i, s)) {
						JOptionPane.showMessageDialog(bcm,
							"The item is unique or already equipped.",
							"",
							JOptionPane.ERROR_MESSAGE);
					}
				}
				
				if (popup != null) {
					popup.dispose();
				}
				
				setVisible(false);
			}
		});
		
		return menuItem;
	}

}
